/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package mb.mbmiddle;

import java.util.Hashtable;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import mb.config.Config;
import mb.config.ConstantsMsg;
import mb.config.DatabaseConfig;
import mb.config.MonitorConfig;
import mb.config.WebserverConfig;
import mb.loadData.LoadDataHandler;
import mb.mbpart.async.AsyncThread;
import mb.mbpart.async.AysncObject;
import mb.mbpart.memory.BaseMemory;
import mb.mbpart.memory.NewsDataIndexer;
import mb.mbpart.memory.QueryMemory;
import mb.monitor.MonitorListener;
import mb.utils.LogText;
import mb.webserver.console.MBWebServer;


/**
 *
 * @author thanh.nguyenchi
 */
public class MbPartMiddle {

    public static final String STR_MIDDLE_MAIN_LOG_FILE_NAME    = "main_log";
    public static final String STR_WEB_LOG_FILENAME             = "webLog";
    public static final String STR_SQL_LOG_FILENAME             = "sqlserverLog";
    public static final String STR_MONITOR_LOG_FILENAME         = "monitorLog";
    public static final String STR_MONITOR_UPDATE_FILENAME      = "updateLog";
    
    public static final String STR_MAIN_CONFIG_FILENAME = "main.cfg";
    public static final String STR_SQLSERVER_DBURL      = "SQLSERVER_DBURL";
    public static final String STR_SQLSERVER_USERNAME   = "SQLSERVER_USERNAME";
    public static final String STR_SQLSERVER_PASSWORD   = "SQLSERVER_PASSWORD";
    public static final String STR_SQLSERVER_PRE_ID   = "PREFERRED_ID";
    public static final String STR_SQLSERVER_PRO_ID   = "PROMOTION_ID";
    public static final String STR_SQLSERVER_SEV_ID   = "SERVICES_ID";
    public static final String STR_SQLSERVER_SUB_ID   = "SUBCOMPANY_ID";    
    public static final String STR_SQLSERVER_PAGE_LIMIT   = "PAGE_LIMIT";
    public static final String STR_CACHE_TIME   = "CACHE_TIME";
    
    public static final String STR_WEBSERVER_PORT           = "WEBSERVER_PORT";
    public static final String STR_WEBSERVER_MAXIDLETIME    = "WEBSERVER_MAXIDLETIME";
    public static final String STR_WEBSERVER_HEADERSIZE     = "WEBSERVER_HEADERSIZE";
    public static final String STR_WEBSERVER_MAXTHREAD      = "WEBSERVER_MAXTHREAD";
    public static final String STR_WEBSERVER_MINTHREAD      = "WEBSERVER_MINTHREAD";
    
    public static final String STR_MONITOR_ENABLE           = "MONITOR_ENABLE";
    public static final String STR_MONITOR_PORT             = "MONITOR_PORT";
    public static final String STR_MONITOR_ADMIN_LIST       = "MONITOR_ADMIN_IP";
    public static final String STR_MONITOR_ADMIN_USER       = "MONITOR_ADMIN_USER";
    
    // Memory
    public static Hashtable<String,BaseMemory> objMemory;  
    public static Hashtable<String,QueryMemory> objQueryMemory;
    public static int queryTime;
    //public static NewsDataIndexer objNewsMemory;
    
    // Queue
    public BlockingQueue<AysncObject> updateQueue;    
    
    // webserver config
    MBWebServer objWebsever;  
    WebserverConfig objWebConfig;    
    
    // update
    AsyncThread objUpdate;
    
    // sqlserver config
    public static DatabaseConfig objDbConfig;    
    
    // monitor config
    int iMonitorEnable;
    MonitorConfig objMonitorConfig;
    
    // log object
    LogText objMainLog;    
    public static LogText objWebLog;
    LogText objDatabaseLog;
    LogText objMonitorLog;
    LogText objUpdateLog;            
    
    public MbPartMiddle(String[] argv){
         try{
            // config
            objWebConfig = new WebserverConfig();
            objMonitorConfig = new MonitorConfig();
            objDbConfig = new DatabaseConfig();
            
            // memory
            objMemory = new Hashtable<String, BaseMemory>();
            objQueryMemory = new Hashtable<String, QueryMemory>();
            //objNewsMemory = new NewsDataIndexer();
            
            // queue
            updateQueue = new LinkedBlockingQueue<AysncObject>();
            
            // init error
            ConstantsMsg.initErrorEnMsg();
            ConstantsMsg.initErrorVnMsg();
            
            // log
            objMainLog = new LogText(STR_MIDDLE_MAIN_LOG_FILE_NAME);
            objWebLog = new LogText(STR_WEB_LOG_FILENAME);
            objDatabaseLog = new LogText(STR_SQL_LOG_FILENAME);
            objMonitorLog = new LogText(STR_MONITOR_LOG_FILENAME);
            objUpdateLog = new LogText(STR_MONITOR_UPDATE_FILENAME);
            
            new Thread(objMainLog).start();
            new Thread(objWebLog).start();
            new Thread(objDatabaseLog).start();
            new Thread(objMonitorLog).start();
            new Thread(objUpdateLog).start();
        }
        catch(Exception ex){
            System.out.println("Create main log (" + STR_MIDDLE_MAIN_LOG_FILE_NAME + ") failed. Error ="+ ex.getMessage());
        }       
    }
    
    public boolean loadConfig(){
        log("Loading config ...");
        // load config from file
        Config objConfig = new Config();
        try{
            objConfig.init(STR_MAIN_CONFIG_FILENAME);
            // config for sql server
            String sSqlUrl = objConfig.getProperty(STR_SQLSERVER_DBURL);            
            if((sSqlUrl == null) || (sSqlUrl.length() <= 0)){
                throw new Exception("Invalid url for SQL Server.");
            }
            objDbConfig.setUrl(sSqlUrl);
            log("    SqlServer url = " + objDbConfig.getUrl());
            
            objDbConfig.setUser(objConfig.getProperty(STR_SQLSERVER_USERNAME));
            objDbConfig.setPass(objConfig.getProperty(STR_SQLSERVER_PASSWORD));
            
            String sPreferred_id = objConfig.getProperty(STR_SQLSERVER_PRE_ID);  
            int pre_id = Integer.parseInt(sPreferred_id);
            if((pre_id <= 0)){
		throw new Exception("Invalid Preferred id = " + sPreferred_id);
            }
            objDbConfig.setPreferred_id(pre_id);
            log("    Preferred id = " + objDbConfig.getPreferred_id());
            
            String sPromotion_id = objConfig.getProperty(STR_SQLSERVER_PRO_ID);  
            int pro_id = Integer.parseInt(sPromotion_id);
            if((pro_id <= 0)){
		throw new Exception("Invalid Promotion id = " + sPromotion_id);
            }            
            objDbConfig.setPromotion_id(pro_id);
            log("    Promotion id = " + objDbConfig.getPromotion_id());
            
            String sServices_id = objConfig.getProperty(STR_SQLSERVER_SEV_ID);  
            int sev_id = Integer.parseInt(sServices_id);
            if((sev_id <= 0)){
		throw new Exception("Invalid Service id = " + sServices_id);
            }            
            objDbConfig.setServices_id(sev_id);
            log("    Services id = " + objDbConfig.getServices_id());
            
            String sSubCompany_id = objConfig.getProperty(STR_SQLSERVER_SUB_ID); 
            int sub_id = Integer.parseInt(sSubCompany_id);
            if((sub_id > 0)){
		throw new Exception("Invalid subCompany id = " + sSubCompany_id);
            }            
            objDbConfig.setSubCompany_id(sub_id);
            log("    subCompan id = " + objDbConfig.getSubCompany_id());
            
            String sPageLimmit = objConfig.getProperty(STR_SQLSERVER_PAGE_LIMIT); 
            int page = Integer.parseInt(sPageLimmit);
            if((page <= 0 )){
		throw new Exception("Invalid page limit = " + sPageLimmit);
            }            
            objDbConfig.setPageLimit(page);
            log("    page limit = " + objDbConfig.getPageLimit());
            
            String sCacheTime = objConfig.getProperty(STR_CACHE_TIME);
            int cache = Integer.parseInt(sCacheTime);
            if((cache <= 0 )){
		throw new Exception("Invalid cache time = " + sCacheTime);
            }            
            this.queryTime = cache;
            log("    cache time = " + this.queryTime);
            
            // config for webserver
            String sPort = objConfig.getProperty(STR_WEBSERVER_PORT);  
            int port = Integer.parseInt(sPort);
            if((port <= 0)){
		throw new Exception("Invalid webserver port = " + sPort);
            }            
            objWebConfig.setPort(port);
            
            log("Config for webserver");
            log("   Webserver port = " + objWebConfig.getPort());
            
            String sMaxIdleTime = objConfig.getProperty(STR_WEBSERVER_MAXIDLETIME);   
            int maxIdleTime = Integer.parseInt(sMaxIdleTime);
            if((maxIdleTime <= 10000)){
		throw new Exception("Invalid maxIdleTime = " + sMaxIdleTime);
            }
            objWebConfig.setMaxIdleTime(maxIdleTime);
            log("      maxIdleTime = " + objWebConfig.getMaxIdleTime());
            
            String sHeaderSize = objConfig.getProperty(STR_WEBSERVER_HEADERSIZE);
            int headerSize = Integer.parseInt(sHeaderSize);
            if((headerSize <= 0)){
		throw new Exception("Invalid headerSize = " + sHeaderSize);
            }
            objWebConfig.setHeaderSize(headerSize);
            log("       headerSize = " + objWebConfig.getHeaderSize());
            
            String sMaxThread = objConfig.getProperty(STR_WEBSERVER_MAXTHREAD);
            int maxThread = Integer.parseInt(sMaxThread);
            if((maxThread <= 0)){
		throw new Exception("Invalid maxThread = " + sMaxThread);
            }
            objWebConfig.setMaxThread(maxThread);
            log("        maxThread = " + objWebConfig.getMaxThread());
            
            String sMinThread = objConfig.getProperty(STR_WEBSERVER_MINTHREAD);
            int minThread = Integer.parseInt(sMinThread);
            if((minThread <= 0)){
		throw new Exception("Invalid minThread = " + sMinThread);
            }
            objWebConfig.setMinThead(minThread);
            log("        minThread = " + objWebConfig.getMinThead());                        

            log("Config for monitor");
            // config for monitor
            
            String iMenable = objConfig.getProperty(STR_MONITOR_ENABLE);
            try{
                iMonitorEnable = Integer.parseInt(iMenable);
            }
            catch(Exception ex){
                log("   monitor enable [err]:" + ex.toString());
                iMonitorEnable = 0;
            }
            
            String sMoPort = objConfig.getProperty(STR_MONITOR_PORT);
            int moPort = Integer.parseInt(sMoPort);
            if((moPort <= 0)){
		throw new Exception("Invalid monitorPort = " + sMoPort);
            }
            objMonitorConfig.setPort(moPort);
            log("     monitor port = " + objMonitorConfig.getPort());  
            
            String listIP = objConfig.getProperty(STR_MONITOR_ADMIN_LIST);
            String listUser = objConfig.getProperty(STR_MONITOR_ADMIN_USER);
            objMonitorConfig.addListUser(listIP, listUser);
            log("         userSize = " + objMonitorConfig.getUserSize());
            
            // set queue
            objDbConfig.setUpdateQueue(updateQueue);
        }
        catch(Exception ex){
            log("        [loadConfig] " + ex.toString());
            objConfig.deinit();
            return false;
        }
        
        objConfig.deinit();
        return true;
        

    }
    
    public void startUpdate(){
        try{
            objUpdate = new AsyncThread(updateQueue,objUpdateLog,objDbConfig,this);            
            new Thread(objUpdate).start();
            //new AsyncThread(updateQueue,objUpdateLog,objDbConfig,this).start();
        }
        catch (Exception ex){   
            log("Update Thead [Error] " + ex.toString());
        }        
    }
    
    public void stopUpdate(){
        try{
            objUpdate.stopUpdate();
        }
        catch (Exception ex){
            
        }
    }
    
    public void startWebsever(){
        try{
            objWebsever = new MBWebServer(objWebConfig);   
            new Thread(objWebsever).start();
        }
        catch (Exception ex){
            log("Webserver [Error]" + ex.toString());
        }        
    }
    
    public void stopWebserver(){
        try{
            if (objWebsever != null){
                objWebsever.stopWebserver();
            }
        }
        catch(Exception ex){
            
        }
    }
    
    public void startMonitor(){
        try{
            if (iMonitorEnable == 1){
                new MonitorListener(objMonitorConfig.getPort(),this,objMonitorLog,objMonitorConfig).start();
            }
        }
        catch (Exception ex){   
            log("Monitor [Error] " + ex.toString());
        }        
    }
    
    public void statInMemory(){
        //objGobalMemory.initMemory();
        log("[InMemory] is staring..");
        try{            
            new LoadDataHandler(objDbConfig,objDatabaseLog,this).start();               
        }
        catch (Exception ex){   
            log("InMemory [Error] " + ex.toString());
        }     
    }
    
    /*
    Function
        Shutdown function
    Parameters
        None
    Return value
        None
    Exceptions
        None
    Algorithm
    */
    public void shutdown(){
        objMainLog.log("Shutting down!");
        if(objMainLog != null) objMainLog.closeLog(); 
        if(objWebLog != null) objWebLog.closeLog(); 
        if(objDatabaseLog != null) objDatabaseLog.closeLog(); 
        if(objMonitorLog != null) objMonitorLog.closeLog(); 
    }
    
    private void log(String sMsg){
        objMainLog.log(sMsg);
    }
     
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        MbPartMiddle objMain = new MbPartMiddle(args);
        
        MiddleServerShutdownHook  customShutdownHook = new MiddleServerShutdownHook(objMain);
	Runtime.getRuntime().addShutdownHook(customShutdownHook);
        
        if (objMain.loadConfig()){  
            // start update
            objMain.startUpdate();
            // start inmemory
            objMain.statInMemory();            
            // start webserver
            objMain.startWebsever();   
            // start monitor thread
            objMain.startMonitor();            
        }
        else{ 
            // invalid
            System.out.println("Invalid config --> MB Middle stop.");
            objMain.shutdown();
            System.exit(0);
        }
    }
}
/*
*Function
*	Custom hook for shutting down the application (cleaning memory, closing server socket, etc...)
*/
class MiddleServerShutdownHook extends Thread {
    MbPartMiddle objMain;
    public MiddleServerShutdownHook(MbPartMiddle objMain){
        this.objMain = objMain;
    }
    public void run() {
        objMain.shutdown();
    }
}
